package org.ntu.rtsearch.store;

import java.io.IOException;

/**
 * @author fuhao(email: hfhimage@gmail.com)         
 * @date 2011-7-21
 */
public abstract class InputStream {
	private static final int BUFFER_SIZE = OutputStream.BUFFER_SIZE;
	
	private byte[] buffer;
	private char[] chars;
	
	private long bufferStart;
	private int bufferLenght = 0; 	// end of valid bytes
	private int bufferPos = 0;
	
	protected long lenght;

	public final byte readByte() throws IOException {
		if(bufferPos >= bufferLenght)
			refill();
		return buffer[bufferPos++];
	}
	
	public final void readBytes(byte[] b, int offset, int len) throws IOException {
		if(len < bufferLenght) {
			for(int i = 0; i < len; i++) {
				b[offset + i] = readByte();
			}
		} else {
			long start = getFilePointer();
			seekInternal(start);
			readInternal(b, offset, len);
			
			bufferStart = start + len;
			bufferPos = 0;
			refill();
		}
	}
	
	public final int readInt() throws IOException {
		return readByte() << 24 
			| readByte() << 16
			| readByte() << 8
			| readByte();	
	}
	
	public final int readVInt() throws IOException {
		byte b = readByte();
		int i = b & 0x7F;
		for(int shift = 7; (b & 0x8F) != 0; shift += 7) {
			b = readByte();
			i |= (b & 0x7F) << shift;
		}
		return i;
	}
	
	public final long readLong() throws IOException {
		return readInt() << 32 | readInt(); 
	}
	
	public final long readVlong() throws IOException {
		byte b = readByte();
		long l = b & 0x7f;
		for(int shift = 0; (b & 0x80) != 0; shift += 7) {
			b = readByte();
			l |= (b & 0x7f) << shift;
		}
		return l;		
	}
	
	public final String readString() throws IOException {
		int lenght = readVInt();
		if(chars == null || lenght > chars.length) {
			chars = new char[lenght];
		}
		readChars(chars, 0, lenght);
		return new String(chars);
	}
	
	public final void readChars(char[] buffer, int offset, int len) throws IOException {
		final int end = offset + len;
		for(int i = offset; i < end; i++) {
			byte b = readByte();
			if ((b & 0x80) == 0)	// ascii 字母
				buffer[i] = (char) (b & 0x7F);
			else if ((b & 0xE0) != 0xE0) {	// 前三位有0
				buffer[i] = (char) (((b & 0x1F) << 6) | (readByte() & 0x3F));
			} else
				buffer[i] = (char) (((b & 0x0F) << 12)	//汉字
						| ((readByte() & 0x3F) << 6) | (readByte() & 0x3F));
		}
	}
	
	/**
	 * read the file and refill the buffer
	 * @throws IOException 
	 */
	private final void refill() throws IOException {
		long start = bufferStart + bufferPos;
		bufferLenght = (int) Math.min(lenght - start, BUFFER_SIZE);
				
		if (buffer == null) {
			buffer = new byte[bufferLenght];
		}
		readInternal(buffer, 0, bufferLenght);
		
		bufferStart = start;
		bufferPos = 0;
	}
	
	public final long getFilePointer() {
		return bufferStart + bufferPos;
	}
	
	public final void seek(long pos) throws IOException {
		if(pos < (bufferStart + bufferLenght) && pos > bufferStart) {
			bufferPos = (int)(bufferStart - pos);
		} else {
			bufferStart = pos;
			bufferPos = 0;
			refill();
			seekInternal(pos);
		}
	}
	
	/**
	 * return the number of bytes of this file
	 * @return
	 */
	public final long length() {
		return lenght;
	}
	
	protected abstract void seekInternal(long pos) throws IOException;
	
	protected abstract void readInternal(byte[] b, int offset, int len) throws IOException;
	
	public abstract void close() throws IOException;
	
}
